package org.fioma.tree;

import java.io.BufferedWriter;
import java.io.IOException;

public class InternalNodeBytes extends SimpleInternalNode<byte[]> {

	public InternalNodeBytes(InternalNode<?> parent, byte[] label) {
		this.parent = parent;
		this.label = label;
	}

	// add a new node in the position of the current label with label word.
	// The letter in the position of the current label will belong to the current label Node
	@Override
	public <W> Leaf<W> addNode(byte pos, W word) {
		byte[] w = (byte[]) word;
		return (Leaf<W>) Tools.addNodeBytes(this, pos, w);
	}

	@Override
	public short getLengthLabel() {
		return label[0];
	}

	public void addWord(byte[] word) {
		if (word != null && word[0] > 0) {
			byte firstLetter = (byte) (word[1] & 0x03);
			Node<byte[]> nextNode = (Node<byte[]>) this.nextNode(firstLetter);
			if (nextNode == null) {
				this.setChild(new LeafBytes(this, word), firstLetter);
			} else {
				byte[] nextWord = (byte[]) nextNode.getLabel();
				short mismatch = Tools.compare(word, nextWord);
				if (mismatch >= 0 && mismatch < word[0]) {
					// word must be splitted
					byte[][] wordSplitted = Tools.splitWord(word, mismatch);
					if (mismatch == nextWord[0]) {
						// next word will not be splitted and a new node will not be created in this section
						nextNode.addWord(wordSplitted[1]);
					} else {
						// new internal node must be created
						nextNode.addNode((byte) mismatch, wordSplitted[1]);
					}

				}
			}
		}
	}

	public int addSuffix(byte[] suffix) {
		if (suffix != null && suffix[0] > 0) {
			byte firstLetter = (byte) (suffix[1] & 0x03);
			Node<byte[]> nextNode = (Node<byte[]>) this.nextNode(firstLetter);
			if (nextNode == null) {
				this.setChild(new LeafBytes(this, suffix), firstLetter);
				return 2;
			} else {
				byte[] nextWord = (byte[]) nextNode.getLabel();
				short mismatch = Tools.compare(suffix, nextWord);
				if (mismatch >= 0 && mismatch < suffix[0]) {
					// word must be splitted
					byte[][] wordSplitted = Tools.splitWord(suffix, mismatch);
					if (mismatch == nextWord[0]) {
						// next word will not be splitted and a new node will not be created in this section
						return nextNode.addSuffix(wordSplitted[1]);
					} else {
						// new internal node must be created
						nextNode.addNode((byte) mismatch, wordSplitted[1]);
						return 2;
					}
				} else {
					return 3;
				}
			}
		}
		return 3;
	}

	public void addWord(char[] word) {
		addWord(Tools.transformToBytes(word));
	}

	@Override
	public void printWords(BufferedWriter bw, byte[] prefix) throws IOException {
		byte[] newPrefix = Tools.joinWords(prefix, label);
		for (Node node : nodes) {
			if (node != null) {
				node.printWords(bw, newPrefix);
			}
		}

	}

	@Override
	public boolean find(byte[] word) {
		if (word == null || word[0] == 0) {
			return true;
		} else {
			byte firstLetter = (byte) (word[1] & 0x03);
			Node<byte[]> nextNode = (Node<byte[]>) this.nextNode(firstLetter);
			if (nextNode == null) {
				return false;
			} else {
				byte[] nextWord = (byte[]) nextNode.getLabel();
				short mismatch = Tools.compare(word, nextWord);
				if (mismatch >= 0 && mismatch < word[0]) {
					// word must be splitted
					byte[][] wordSplitted = Tools.splitWord(word, mismatch);
					if (mismatch == nextWord[0]) {
						// next word will not be splitted and a new node will not be created in this section
						return nextNode.find(wordSplitted[1]);
					} else {
						// new internal node must be created
						return false;
					}
				} else {
					return true;
				}
			}
		}
	}
}
